// wifi_app_task_v1.c - 集成配网管理模块的WiFi任务
#include "app_config.h"
#include "system/includes.h"
#include "wifi/wifi_connect.h"
#include "lwip.h"
#include "dhcp_srv/dhcp_srv.h"
#include "event/net_event.h"
#include "net/assign_macaddr.h"
#include "syscfg_id.h"
#include "lwip/sockets.h"

// 新的配网模块头文件
#include "custom/provisioning/wifi_prov_mgr_v1.h"

static char save_ssid_flag, request_connect_flag;

// 选择其中一种开机默认的模式测试
#define AP_MODE_TEST
// #define STA_MODE_TEST

#define FORCE_DEFAULT_MODE 1 // 配置wifi_on之后的模式,0为使用最后记忆的模式, 1为强制默认模式, 3-200为STA连接超时时间多少秒,如果超时都连接不上就连接最后记忆的或者最优网络

#define AP_SSID "AIBOT_"     // 配置 AP模式的SSID前缀
#define AP_PWD ""            // 配置 AP模式的密码
#define STA_SSID "golang"    // 配置 STA模式的SSID
#define STA_PWD "1234567890" // 配置 STA模式的密码
#define CONNECT_BEST_SSID 0  // 配置如果啟動WIFI后在STA模式下, 是否挑选连接记忆过的信号最优WIFI

// =============== 配网管理模块集成 ===============

static void on_store_sta_info(const char *ssid, const char *pwd);
static void on_clear_sta_info(void);
static void on_wifi_sta_mode(const char *ssid, const char *pwd);
static void on_wifi_ap_mode(void);
static void on_start(void);
static void on_stop(void);
static void on_finished(void);
static void on_timeout(void);

// 定义配网管理模块配置
wifi_prov_cfg_t wifi_prov_config = {
    .ap_ssid_prefix = "AIBOT_",
    .ap_password = "",
    .ops = {
        .on_wifi_ap_mode = on_wifi_ap_mode,
        .on_wifi_sta_mode = on_wifi_sta_mode,
        .on_clear_sta_info = on_clear_sta_info,
        .on_store_sta_info = on_store_sta_info,
        .on_start = on_start,
        .on_stop = on_stop,
        .on_finished = on_finished,
        .on_timeout = on_timeout,
    },
};

static void wifi_softap_set_lan_setting_info(void)
{
    struct lan_setting lan_setting_info = {
        .WIRELESS_IP_ADDR0 = 192,
        .WIRELESS_IP_ADDR1 = 168,
        .WIRELESS_IP_ADDR2 = 8,
        .WIRELESS_IP_ADDR3 = 1,

        .WIRELESS_NETMASK0 = 255,
        .WIRELESS_NETMASK1 = 255,
        .WIRELESS_NETMASK2 = 255,
        .WIRELESS_NETMASK3 = 0,

        .WIRELESS_GATEWAY0 = 192,
        .WIRELESS_GATEWAY1 = 168,
        .WIRELESS_GATEWAY2 = 8,
        .WIRELESS_GATEWAY3 = 1,

        .SERVER_IPADDR1 = 192,
        .SERVER_IPADDR2 = 168,
        .SERVER_IPADDR3 = 8,
        .SERVER_IPADDR4 = 1,

        .CLIENT_IPADDR1 = 192,
        .CLIENT_IPADDR2 = 168,
        .CLIENT_IPADDR3 = 8,
        .CLIENT_IPADDR4 = 2,

        .SUB_NET_MASK1 = 255,
        .SUB_NET_MASK2 = 255,
        .SUB_NET_MASK3 = 255,
        .SUB_NET_MASK4 = 0,
    };
    net_set_lan_info(&lan_setting_info);

    // 启动DNS服务
    dhcps_offer_dns();
}

static void wifi_sta_save_ssid(void)
{
    if (save_ssid_flag)
    {
        save_ssid_flag = 0;

        struct wifi_mode_info info;
        info.mode = STA_MODE;
        wifi_get_mode_cur_info(&info);
        wifi_store_mode_info(STA_MODE, info.ssid, info.pwd);
    }
}

void wifi_return_sta_mode(void)
{
    if (!wifi_is_on())
    {
        return;
    }

    int ret;
    struct wifi_mode_info info;
    info.mode = STA_MODE;
    ret = wifi_get_mode_stored_info(&info);
    if (ret)
    {
        // 如果没保存过SSID
        info.ssid = STA_SSID;
        info.pwd = STA_PWD;
    }

    wifi_clear_scan_result();          // 根据扫描结果连接信号最优ssid之前先清除之前结果,防止之前最优信号的ssid已下线
    wifi_set_sta_connect_best_ssid(1); // 自动连接保存过的最佳WIFI
    save_ssid_flag = 0;
    wifi_enter_sta_mode(info.ssid, info.pwd);
}

static void on_store_sta_info(const char *ssid, const char *pwd)
{
}

static void on_clear_sta_info(void)
{
}

static void on_wifi_sta_mode(const char *ssid, const char *pwd)
{
    wifi_set_sta_connect_timeout(30); // 配置STA模式连接超时后事件回调通知时间

    // 正常启动，使用STA模式
    struct wifi_store_info info = {0};
    info.mode = STA_MODE;
    strncpy((char *)info.ssid[info.mode - STA_MODE],
            (const char *)ssid,
            sizeof(info.ssid[info.mode - STA_MODE]) - 1);
    strncpy((char *)info.pwd[info.mode - STA_MODE],
            (const char *)pwd,
            sizeof(info.pwd[info.mode - STA_MODE]) - 1);

    // 强制使用设定的SSID
    info.connect_best_network = 0;

    // 强制使用默认模式及存储
    wifi_set_default_mode(&info, 1, 1);
    printf("[wifi app task] Wifi default mode: sta mode.\n");
}

static void on_wifi_ap_mode(void)
{
    // 需要配网，启动AP模式
    wifi_softap_set_lan_setting_info();

    // 组装AP名称
    uint8_t mac_addr[6];
    char ap_ssid[64];
    extern int init_net_device_mac_addr(char *macaddr, char ap_mode);
    init_net_device_mac_addr((char *)mac_addr, 1);
    sprintf((char *)ap_ssid, "%s%02X%02X%02X%02X%02X%02X",
            wifi_prov_config.ap_ssid_prefix,
            mac_addr[0], mac_addr[1], mac_addr[2],
            mac_addr[3], mac_addr[4], mac_addr[5]);

    // 设置默认模式参数
    struct wifi_store_info info = {0};
    info.mode = AP_MODE;
    strncpy((char *)info.ssid[info.mode - STA_MODE],
            (const char *)ap_ssid,
            sizeof(info.ssid[info.mode - STA_MODE]) - 1);
    strncpy((char *)info.pwd[info.mode - STA_MODE],
            (const char *)wifi_prov_config.ap_password,
            sizeof(info.pwd[info.mode - STA_MODE]) - 1);

    // 强制使用默认模式
    wifi_set_default_mode(&info, 1, 0);
    printf("[wifi app task] Wifi default mode: ap mode.\n");
}

static void on_start(void)
{
    printf("wifi ap config started.\n");
}

static void on_stop(void)
{
    printf("wifi ap config stopped.\n");
}

static void on_finished(void)
{
    printf("[wifi app task] wifi ap config finished\n");

    struct wifi_mode_info info = {0};
    info.mode = STA_MODE;
    wifi_get_mode_cur_info(&info);
    printf("[wifi app task][wifi_get_mode_cur_info] mode %d, ssid: %s, pwd: %s\n",
           info.mode, info.ssid, info.pwd);

    // 覆盖默认
}

static void on_timeout(void)
{
    printf("wifi ap config timeout.\n");
}

// =============== 原有WiFi功能 ===============

void wifi_sta_connect(char *ssid, char *pwd, char save)
{
    save_ssid_flag = save;
    request_connect_flag = 1;
    wifi_set_sta_connect_best_ssid(0);
    wifi_enter_sta_mode(ssid, pwd);
}

// =============== WiFi事件处理handle ===============

static void on_wifi_evt_init(void *network_ctx, enum WIFI_EVENT event)
{
}

static void on_wifi_evt_ap_start(void *network_ctx, enum WIFI_EVENT event)
{
    // 配置DNS服务器指向设备IP
    u32 dns_server = wifi_prov_config.captive_portal_ip;
    dns_setserver(0, &dns_server);
    // 启动DHCP服务器
    dhcps_init(0);
}

static void on_wifi_evt_ap_stop(void *network_ctx, enum WIFI_EVENT event)
{
    // 停止DHCP服务器
    dhcps_uninit();
}

// =============== WiFi事件处理 ===============
static int wifi_event_callback(void *network_ctx, enum WIFI_EVENT event)
{
    struct net_event net = {0};
    net.arg = "net";
    int ret = 0;

    // 先转发给配网管理模块
    wifi_prov_mgr_on_wifi_evt(network_ctx, event);

    switch (event)
    {
        case WIFI_EVENT_MODULE_INIT:
            puts("|network_user_callback->WIFI_EVENT_MODULE_INIT\n");
            on_wifi_evt_init(network_ctx, event);
            break;

        case WIFI_EVENT_MODULE_START:
            puts("|network_user_callback->WIFI_EVENT_MODULE_START\n");
            break;

        case WIFI_EVENT_MODULE_STOP:
            puts("|network_user_callback->WIFI_EVENT_MODULE_STOP\n");
            break;

        case WIFI_EVENT_AP_START:
            printf("|network_user_callback->WIFI_EVENT_AP_START,CH=%d\n", wifi_get_channel());
            on_wifi_evt_ap_start(network_ctx, event);
            break;

        case WIFI_EVENT_AP_STOP:
            puts("|network_user_callback->WIFI_EVENT_AP_STOP\n");
            on_wifi_evt_ap_stop(network_ctx, event);
            break;

        case WIFI_EVENT_STA_START:
            puts("|network_user_callback->WIFI_EVENT_STA_START\n");
            break;

        case WIFI_EVENT_MODULE_START_ERR:
            puts("|network_user_callback->WIFI_EVENT_MODULE_START_ERR\n");
            break;

        case WIFI_EVENT_STA_STOP:
            puts("|network_user_callback->WIFI_EVENT_STA_STOP\n");
            break;

        case WIFI_EVENT_STA_DISCONNECT:
            puts("|network_user_callback->WIFI_STA_DISCONNECT\n");
            net.event = NET_EVENT_DISCONNECTED;
            net_event_notify(NET_EVENT_FROM_USER, &net);

#ifndef WIFI_MODE_CYCLE_TEST
            if (!request_connect_flag)
            { // 如果是应用程序主动请求连接导致断线就不需要发送重连事件, 否则像信号不好导致断线的原因就发送重连事件
                net.event = NET_EVENT_DISCONNECTED_AND_REQ_CONNECT;
                net_event_notify(NET_EVENT_FROM_USER, &net);
            }
#endif
            break;

        case WIFI_EVENT_STA_SCANNED_SSID:
            puts("|network_user_callback->WIFI_EVENT_STA_SCANNED_SSID\n");
            break;

        case WIFI_EVENT_STA_SCAN_COMPLETED:
            puts("|network_user_callback->WIFI_STA_SCAN_COMPLETED\n");
            break;

        case WIFI_EVENT_STA_CONNECT_SUCC:
            printf("|network_user_callback->WIFI_STA_CONNECT_SUCC,CH=%d\r\n", wifi_get_channel());
            break;

        case WIFI_EVENT_MP_TEST_START:
            puts("|network_user_callback->WIFI_EVENT_MP_TEST_START\n");
            break;
        case WIFI_EVENT_MP_TEST_STOP:
            puts("|network_user_callback->WIFI_EVENT_MP_TEST_STOP\n");
            break;

        case WIFI_EVENT_STA_CONNECT_TIMEOUT_NOT_FOUND_SSID:
            puts("|network_user_callback->WIFI_STA_CONNECT_TIMEOUT_NOT_FOUND_SSID\n");
            net.event = NET_CONNECT_TIMEOUT_NOT_FOUND_SSID;
            net_event_notify(NET_EVENT_FROM_USER, &net);
            break;

        case WIFI_EVENT_STA_CONNECT_ASSOCIAT_FAIL:
            puts("|network_user_callback->WIFI_STA_CONNECT_ASSOCIAT_FAIL .....\n");
            net.event = NET_CONNECT_ASSOCIAT_FAIL;
            net_event_notify(NET_EVENT_FROM_USER, &net);
            break;

        case WIFI_EVENT_STA_CONNECT_ASSOCIAT_TIMEOUT:
            puts("|network_user_callback->WIFI_STA_CONNECT_ASSOCIAT_TIMEOUT .....\n");
            break;

        case WIFI_EVENT_STA_NETWORK_STACK_DHCP_SUCC:
            puts("|network_user_callback->WIFI_STA_NETWPRK_STACK_DHCP_SUCC\n");

            // 有些使用了加密的路由器刚获取IP地址后前几个包都会没响应，怀疑路由器没转发成功
            void connect_broadcast(void);
            connect_broadcast();

            wifi_sta_save_ssid(); // 确认获取IP成功再真正去考虑要不要保存配置, 否则如果配置有误就保存的情况下导致下次连不上WIFI

            request_connect_flag = 0;
            net.event = NET_EVENT_CONNECTED;
            net_event_notify(NET_EVENT_FROM_USER, &net);
            break;

        case WIFI_EVENT_STA_NETWORK_STACK_DHCP_TIMEOUT:
            puts("|network_user_callback->WIFI_STA_NETWPRK_STACK_DHCP_TIMEOUT\n");
            break;

        case WIFI_EVENT_P2P_START:
            puts("|network_user_callback->WIFI_EVENT_P2P_START\n");
            break;
        case WIFI_EVENT_P2P_STOP:
            puts("|network_user_callback->WIFI_EVENT_P2P_STOP\n");
            break;
        case WIFI_EVENT_P2P_GC_DISCONNECTED:
            puts("|network_user_callback->WIFI_EVENT_P2P_GC_DISCONNECTED\n");
            break;
        case WIFI_EVENT_P2P_GC_NETWORK_STACK_DHCP_SUCC:
            puts("|network_user_callback->WIFI_EVENT_P2P_GC_NETWORK_STACK_DHCP_SUCC\n");
            break;
        case WIFI_EVENT_P2P_GC_NETWORK_STACK_DHCP_TIMEOUT:
            puts("|network_user_callback->WIFI_EVENT_P2P_GC_NETWORK_STACK_DHCP_TIMEOUT\n");
            break;

        case WIFI_EVENT_SMP_CFG_START:
            puts("|network_user_callback->WIFI_EVENT_SMP_CFG_START\n");
            break;
        case WIFI_EVENT_SMP_CFG_STOP:
            puts("|network_user_callback->WIFI_EVENT_SMP_CFG_STOP\n");
            break;
        case WIFI_EVENT_SMP_CFG_TIMEOUT:
            puts("|network_user_callback->WIFI_EVENT_SMP_CFG_TIMEOUT\n");
            net.event = NET_EVENT_SMP_CFG_TIMEOUT;
            net_event_notify(NET_EVENT_FROM_USER, &net);
            break;
        case WIFI_EVENT_SMP_CFG_COMPLETED:
            puts("|network_user_callback->WIFI_EVENT_SMP_CFG_COMPLETED\n");
            net.event = NET_SMP_CFG_COMPLETED;
            net_event_notify(NET_EVENT_FROM_USER, &net);
            break;

        case WIFI_EVENT_PM_SUSPEND:
            puts("|network_user_callback->WIFI_EVENT_PM_SUSPEND\n");
            break;
        case WIFI_EVENT_PM_RESUME:
            puts("|network_user_callback->WIFI_EVENT_PM_RESUME\n");
            break;
        case WIFI_EVENT_AP_ON_ASSOC:;
            struct eth_addr *hwaddr = (struct eth_addr *)network_ctx;
            printf("WIFI_EVENT_AP_ON_ASSOC hwaddr = %02x:%02x:%02x:%02x:%02x:%02x \r\n\r\n",
                   hwaddr->addr[0], hwaddr->addr[1], hwaddr->addr[2], hwaddr->addr[3], hwaddr->addr[4], hwaddr->addr[5]);
            break;
        case WIFI_EVENT_AP_ON_DISCONNECTED:
            struct ip4_addr ipaddr;
            hwaddr = (struct eth_addr *)network_ctx;
            dhcps_get_ipaddr(hwaddr->addr, &ipaddr);
            printf("WIFI_EVENT_AP_ON_DISCONNECTED hwaddr = %02x:%02x:%02x:%02x:%02x:%02x, ipaddr = [%d.%d.%d.%d] \r\n\r\n",
                   hwaddr->addr[0], hwaddr->addr[1], hwaddr->addr[2], hwaddr->addr[3], hwaddr->addr[4], hwaddr->addr[5],
                   ip4_addr1(&ipaddr), ip4_addr2(&ipaddr), ip4_addr3(&ipaddr), ip4_addr4(&ipaddr));
            break;

        case WIFI_EVENT_STA_IP_GOT_IPV6_SUCC:
            printf("network_user_callback->WIFI_EVENT_STA_IP_GOT_IPV6_SUCC");
            break;
        default:
            break;
    }

    return ret;
}

static void wifi_app_task(void *priv)
{
    // 初始化配网模块
    wifi_prov_mgr_init(&wifi_prov_config);

    wifi_set_store_ssid_cnt(2);
    wifi_set_event_callback(wifi_event_callback);
    wifi_on();
}

static int demo_wifi(void)
{
    // 打开电源
    extern void custom_wifi_power_on(void);
    custom_wifi_power_on();

    return thread_fork("wifi_app_task", 10, 1000, 0, NULL, wifi_app_task, NULL);
}
late_initcall(demo_wifi);
